import gtk
import locale
import datetime

from ui import *
from soap import *

from ui.GladeBase import GladeBase
from utils import logger, envir, check_authorization, BOOKSHOP

NO_ENTITY = -1

ORDER_NEW = u'new'
ORDER_PREPARING = u'preparing'
ORDER_DELIVERING = u'delivering'
ORDER_COMPLETED = u'completed'
ORDER_REJECTED = u'rejected'
ORDER_STATUS = (ORDER_NEW, ORDER_PREPARING, ORDER_DELIVERING,
                ORDER_COMPLETED, ORDER_REJECTED)

def total_amount(price, quantity, discount):
    total_amount = (quantity * price) * (1 - discount / 100)
    return total_amount
    
class ManageOrder(GladeBase):
    def __init__(self, query=None, top_component=None):
        logger.debug("UI: %s instance created" % self.__class__)
        GladeBase.__init__(self, 'manage_order_frame', top_component)
        self.order_frame_hpaned.set_position(400)
        
        # check if i am a bookshop..
        id = None
        if check_authorization(envir.get_user(), BOOKSHOP):
            id = envir.get_user()['id']
            self.bookshop_combobox.set_sensitive(False)
            self.status_combobox.set_sensitive(False)
            
        # Setting up order list
        self.order_list = gtk.ListStore(int, str, str, str, object)
        self.order_list.set_sort_column_id(3, gtk.SORT_DESCENDING)
        try:
            for order in client.get_orders(id):
                self._append_order(order)
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)
        
        self.order_treeview.set_model(self.order_list)

        # set up bookshops combobox
        bookshop_list = gtk.ListStore(int, str)
        try:
            for bs in client.get_bookshops(id):
                bookshop_list.append([bs.id, bs.company_name])
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)
            
        self.bookshop_combobox.set_model(bookshop_list)
        
        # set up status combobox
        status_list = gtk.ListStore(str)
        for status in ORDER_STATUS:
            status_list.append([status])
        self.status_combobox.set_model(status_list)
        
        # set up treeview for product list
        self.product_list = gtk.ListStore(int, str, int, float, float)
        self.product_list.set_sort_column_id(1, gtk.SORT_ASCENDING)
        try:
            for product in client.get_products():
                row = [product.id, product.name, product.quantity,
                       product.price, product.discount]
                self.product_list.append(row)
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)
        
        self.product_treeview.set_model(self.product_list)
        
        # setup treeview for order details
        self.detail_list = gtk.ListStore(str, int, float, float, float, int)
        self.detail_list.set_sort_column_id(0, gtk.SORT_ASCENDING)
        self.detail_treeview.set_model(self.detail_list)
        
        product_tvc = gtk.TreeViewColumn('Product') 
        quantity_tvc = gtk.TreeViewColumn('Quantity')
        price_tvc = gtk.TreeViewColumn('Price')
        discount_tvc = gtk.TreeViewColumn('Discount')
        total_tvc = gtk.TreeViewColumn('Total')
        
        self.detail_treeview.append_column(product_tvc)
        self.detail_treeview.append_column(quantity_tvc)
        self.detail_treeview.append_column(price_tvc)
        self.detail_treeview.append_column(discount_tvc)
        self.detail_treeview.append_column(total_tvc)
        
        product_cell = gtk.CellRendererText()
        quantity_cell = gtk.CellRendererSpin()
        quantity_cell.set_property("editable", True)
        quantity_cell.set_property("adjustment", 
            gtk.Adjustment(upper=9999, step_incr=1, page_incr=0, page_size=0))
        price_cell = gtk.CellRendererText()
        discount_cell = gtk.CellRendererText()
        total_cell = gtk.CellRendererText()
        
        product_tvc.pack_start(product_cell, True)
        product_tvc.add_attribute(product_cell, 'text', 0)
        
        quantity_tvc.pack_start(quantity_cell, True)
        quantity_tvc.add_attribute(quantity_cell, 'text', 1)
        
        price_tvc.pack_start(price_cell, False)
        price_tvc.add_attribute(price_cell, 'text', 2)
        
        discount_tvc.pack_start(discount_cell, False)
        discount_tvc.add_attribute(discount_cell, 'text', 3)
        
        total_tvc.pack_start(total_cell, False)
        total_tvc.add_attribute(total_cell, 'text', 4)
        
        # Connecting events
        quantity_cell.connect('edited', self.on_quantity_changed)
        self.detail_list.connect('row-changed', self.on_order_changed)
        
        # Init all gui's component
        self._init_gui_components()
        
        self.entity_key = NO_ENTITY
    
    def _append_order(self, order):
        """
        Add an order to the list
        """
        self.order_list.append([order.id, order.bookshop.company_name, order.status,
                                order.date, order])
        
    def _update_order(self, row, order):
        """
        Update an order row in model with new value
        """
        self.order_list.set(row.iter, 1, order.bookshop.company_name,
                            2, order.status, 3, order.date, 4, order)
    
    def _init_gui_components(self):
        """
        Intialize gui components as needed. This is useful in the contructor or 
        binded with particular actions (for instance, a "clear" button, or "new" button) 
        """
        self.bookshop_combobox.set_active(-1)
        if check_authorization(envir.get_user(), BOOKSHOP):
            self.bookshop_combobox.set_active(0)
        
        self.status_combobox.set_active(0)
        
        iter = self.detail_list.get_iter_first()
        while iter:
            self.detail_list.remove(iter)
            iter = self.detail_list.get_iter_first()
        
        self.total_text.set_text("")
        self.order_total = 0.0
        self.apply_button.set_sensitive(False)
        self.order_form.set_sensitive(True)

    def _retrieve_components(self):
        """
        Retrieve all used gui components from builder. This method is called in the
        GladeBase constructor
        """
        self.get_from_builder('apply_button')
        self.get_from_builder('order_treeview')
        self.get_from_builder('detail_treeview')
        self.get_from_builder('product_treeview')
        self.get_from_builder('bookshop_combobox')
        self.get_from_builder('status_combobox')
        self.get_from_builder('total_text')
        self.get_from_builder('order_form_vbox', 'order_form')
        self.get_from_builder('order_frame_hpaned')
        
    def _has_valid_data(self):
        """
        Formal control for data in the input form.
        @return: boolean. 'True' when data is valid
        """
        iter = self.bookshop_combobox.get_active_iter()
        if not iter:
            generic_data_error("Please select a bookshop", self.bookshop_combobox)
            return False
        
        iter = self.detail_list.get_iter_first()
        if not iter:
            generic_data_error("Order is empty", self.detail_treeview)
            return False
        
        return True
        
    def _save_data(self):
        """
        Save changes to db. 
        @return: 'True' if data have been successfully saved 
        """
        if not self._has_valid_data():
            return False
        
        # manage Order data object
        if self.entity_key == NO_ENTITY:
            order = OrderSoap()
        else:
            row = search(self.order_list, (0, self.entity_key))
            order = row[4]
            
        iter = self.bookshop_combobox.get_active_iter()
        bs = BookshopSoap()
        bs.id = self.bookshop_combobox.get_model().get_value(iter, 0)
        bs.company_name = self.bookshop_combobox.get_model().get_value(iter, 1)
        order.bookshop = bs
        order.status = self.status_combobox.get_active_text()
        order.total_amount = self.order_total
        order.date = datetime.datetime.now().replace(microsecond=0)
        
        order.detail = []
            
        for (name, quantity, price, discount, total, id) in self.detail_list:
            row = search(self.product_list, (0, id))
            det = OrderDetailSoap()
            det.product = id
            det.quantity = quantity
            det.pamount = row[2]
            order.detail.append(det)
        
        # save changes
        try:
            order.id = client.save_order(order)
            if not order.id > 0:
                raise Exception("Problem occurred on server during save")
            
            if self.entity_key == NO_ENTITY:
                self._append_order(order)
            else:
                # update row in list
                row = search(self.order_list, (0, order.id))
                self._update_order(row, order)
            
            self.entity_key = order.id
            self.apply_button.set_sensitive(False)
            return True
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)
            return False
    
    def _can_edit_order(self, order):
        if order.status in (ORDER_NEW, ORDER_PREPARING):
            return True
        
        return False
    
    # Signals
    def on_new_button_clicked(self, object, data=None):
        if self.apply_button.get_property('sensitive'):
            res = get_save_dialog(self.get_top_component(), "Save changes?")
            if res == gtk.RESPONSE_OK:
                if not self._save_data():
                    return
                
        self._init_gui_components()
        self.order_treeview.get_selection().unselect_all()
        self.entity_key = NO_ENTITY
    
    def on_apply_button_clicked(self, object, data=None):
        self._save_data()
    
    def on_delete_button_clicked(self, object, data=None):
        (model, iter) = self.order_treeview.get_selection().get_selected()
        if not iter:
            return
        
        order = model.get_value(iter, 4)
        if not self._can_edit_order(order):
            generic_data_error("This order is %s and can't be deleted" % order.status,
                               self.order_treeview)
            return
        
        try:
            client.delete_order(order)
            model.remove(iter)
            
            self.product_list.clear()
            try:
                for product in client.get_products():
                    row = [product.id, product.name, product.quantity,
                           product.price, product.discount]
                    self.product_list.append(row)
            except Exception as e:
                get_error_dialog(self.get_top_component(), e)
        
            self._init_gui_components()
            self.entity_key = NO_ENTITY
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)

    def on_order_changed(self, *args, **kwargs):
        self.apply_button.set_sensitive(True)
    
    def on_order_treeview_row_activated(self, treeview, path, view_column, data=None):
        if self.apply_button.get_property('sensitive'):
            res = get_save_dialog(self.get_top_component(), "Save changes?")
            if res == gtk.RESPONSE_OK:
                if not self._save_data():
                    return
                    
        self._init_gui_components()
        
        order = self.order_list[path][4]
        self.entity_key = order.id

        row = search(self.bookshop_combobox.get_model(), (0, order.bookshop.id))
        self.bookshop_combobox.set_active_iter(row.iter)
        
        index = ORDER_STATUS.index(order.status)
        self.status_combobox.set_active(index)
        
        for detail in order.detail:
            (id, name, amount, price, discount) = search(self.product_list, (0, detail.product))
            detail_total = total_amount(price, detail.quantity, discount)
            row = [name, detail.quantity, price, 
                   discount, round(detail_total, 2), id]
            self.order_total += detail_total
            self.detail_list.append(row)
        
        self.total_text.set_text(locale.currency(self.order_total))
        
        if check_authorization(envir.get_user(), BOOKSHOP) and not self._can_edit_order(order):
            self.order_form.set_sensitive(False)
        
        self.apply_button.set_sensitive(False)

    def on_remove_button_clicked(self, object, data=None):
        (model, iter) = self.detail_treeview.get_selection().get_selected()
        if not iter:
            return
        
        self.order_total -= model.get_value(iter, 4)
        
        # Increase product quantity
        id = model.get_value(iter, 5)
        row = search(self.product_list, (0, id))
        row[2] += model.get_value(iter, 1)
        
        model.remove(iter)
        self.total_text.set_text(locale.currency(self.order_total))
        
        self.on_order_changed()
    
    def on_add_button_clicked(self, object, data=None):
        (model, iter) = self.product_treeview.get_selection().get_selected()
        if not iter:
            return
        
        (id, name, amount, price, discount) = model[model.get_path(iter)]
        if amount == 0:
            generic_data_error("This product is unavailable at the moment",
                                self.detail_treeview)
            return
        
        # Avoid duplications
        if search(self.detail_list, (5, id)):
            return
        
        detail_total = total_amount(price, 1, discount)
        row = [name, 1, price, discount, detail_total, id]
        self.detail_list.append(row)
        self.order_total += detail_total
        self.total_text.set_text(locale.currency(self.order_total))
        
        # Decrease product quantity
        model[model.get_path(iter)][2] -= 1
    
    def on_quantity_changed(self, object, path, new_val, data=None):
        (name, quantity, price, discount, total, id) = self.detail_list[path]
        new_val = int(new_val)
        if new_val == quantity: 
            return
        
        diff = new_val - quantity
        row = search(self.product_list, (0, id)) # select the product row
        if new_val > quantity:
            if diff > row[2]:
                generic_data_error("The amount chosen is not available for this item", 
                               self.detail_treeview)
                return
        
        self.order_total -= float(total)
        
        # Change product quantity
        row[2] -= diff
        
        total = total_amount(float(price), new_val, float(discount))
        self.order_total += total
        self.total_text.set_text(locale.currency(self.order_total))
        
        self.detail_list[path] = (name, new_val, price, discount, total, id)
        